package com.peaksport.framework.extend.schedule;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.CronTask;
import org.springframework.scheduling.config.ScheduledTask;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;

import com.peaksport.framework.exception.PKDevException;
import com.peaksport.framework.extend.spring.PKApplicationContext;
import com.peaksport.framework.util.PKInvokeUtils;
import com.peaksport.framework.util.PKPublicUtils;
import com.peaksport.framework.uuid.PKUuid;
import com.peaksport.pkms.system.entity.ScheduleDefEntity;
import com.peaksport.pkms.system.venum.ScheduleDefStatusEnum;

@Configuration
@EnableScheduling
public class PKScheduleConfiguration implements SchedulingConfigurer {
	
	/**后台事务定义监控自动刷新间隔(秒)*/
	@Value("${peak.schedule.monitor.interval:300}")
	private String monitorInterval;
	/**后台事务执行线程数*/
	@Value("${peak.schedule.thread.poolsize:20}")
	private String poolSize;
	@Autowired
	private PKScheduleService service;
	/**scheduled task 注册器 */
	private ScheduledTaskRegistrar taskRegistrar;
	/**后台事务定义列表 */
	private Set<ScheduledTask> cacheTask = new HashSet<>();


	@Override
	public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
		taskRegistrar.setScheduler(taskExecutor());
		this.taskRegistrar = taskRegistrar;
		//添加自动监控后台事务定义
		long l = Long.valueOf(monitorInterval) * 1000;
		taskRegistrar.addFixedDelayTask(new ScheduleDefMonitor(), l);
	}
	
	

	@Bean(destroyMethod = "shutdown")
	public Executor taskExecutor() {
		// 在实际项目中，我们一个系统可能会定义多个定时任务。那么多个定时任务之间是可以相互独立且可以并行执行的。
		// 通过查看org.springframework.scheduling.config.ScheduledTaskRegistrar源代码，发现spring默认会创建一个单线程池。这样对于我们的多任务调度可能会是致命的，当多个任务并发（或需要在同一时间）执行时，任务调度器就会出现时间漂移，任务执行时间将不确定。
		int i = Integer.valueOf(poolSize);
		return Executors.newScheduledThreadPool(i);
	}
	
	/**
	 * 
	 * 后台事务监控线程类
	 *
	 * @author jinbin_guo 2019年8月3日
	 *
	 */
	private class ScheduleDefMonitor implements Runnable {
		@Override
		public void run() {
			//启用状态后台事务列表
			Map<String,ScheduleDefEntity> mapScheduleDef_enabled = new HashMap<>();
			List<ScheduleDefEntity> lstScheduleDef = service.getScheduleDefColByStatus(ScheduleDefStatusEnum.ENABLED);
			for (int i = 0, len = lstScheduleDef.size(); i < len;i++) {
				ScheduleDefEntity scheduleDefEntity = lstScheduleDef.get(i);
				String microService = scheduleDefEntity.getMicroService();
				if (!PKPublicUtils.isEmpty(microService) && !PKApplicationContext.getMicroService().equals(microService)) continue;
				String id = scheduleDefEntity.getId();
				String key = id;
				mapScheduleDef_enabled.put(key, scheduleDefEntity);
			}
			Iterator<ScheduledTask> itCacheTask = cacheTask.iterator();
			while (itCacheTask.hasNext()) {
				ScheduledTask scheduledTask = itCacheTask.next();
				Runnable runnable = scheduledTask.getTask().getRunnable();
				if (runnable instanceof PKCornScheduleRunnable) {
					ScheduleDefEntity scheduleDefEntity_registered = ((PKCornScheduleRunnable)runnable).getScheduleDefEntity();
					String id_registered = scheduleDefEntity_registered.getId();
					String cron_registered = scheduleDefEntity_registered.getCron();
					String runnnable_registered = scheduleDefEntity_registered.getRunnableClass();
					String microService_registered = scheduleDefEntity_registered.getMicroService();
					boolean mutex_registered = scheduleDefEntity_registered.isMutex();
					String key = id_registered;
					ScheduleDefEntity scheduleDefEntity_enabled = mapScheduleDef_enabled.get(key);
					//如果已注册的后台事务已不在启用后台事务列表,则直接将取消注册
					if (scheduleDefEntity_enabled == null) {
						scheduledTask.cancel();
						itCacheTask.remove();
						continue;
					}
					String cron_enabled = scheduleDefEntity_enabled.getCron();
					String runnnable_enabled = scheduleDefEntity_enabled.getRunnableClass();
					String microService_enabled = scheduleDefEntity_enabled.getMicroService();
					boolean mutex_enabled = scheduleDefEntity_enabled.isMutex();
					
					//如果已注册的后台事务在启用后台事务列表,且runnable类&cron没有变化,则更新下最新的ScheduleDefEntity
					if (PKPublicUtils.equals(runnnable_registered, runnnable_enabled) && PKPublicUtils.equals(cron_registered, cron_enabled) &&
						PKPublicUtils.equals(microService_registered, microService_enabled) && PKPublicUtils.equals(mutex_registered, mutex_enabled)) {
						mapScheduleDef_enabled.remove(key);
					} else {
						//如果已注册的后台事务在启用后台事务列表,且runnable类&cron发生了变化,则先取消注册,待后续重新注册
						scheduledTask.cancel();
						itCacheTask.remove();
					}
				}
			}
			
			//如果还存在启用状态的后台事务未注册
			if (!mapScheduleDef_enabled.isEmpty()) {
				Collection<ScheduleDefEntity> scheduleDefEntityCol =  mapScheduleDef_enabled.values();
				Iterator<ScheduleDefEntity> itScheduleDefEntity = scheduleDefEntityCol.iterator();
				while (itScheduleDefEntity.hasNext()) {
					ScheduleDefEntity scheduleDefEntity = itScheduleDefEntity.next();
					String cron = scheduleDefEntity.getCron();
				    String runnableClass = scheduleDefEntity.getRunnableClass();
				    Object runnable = PKInvokeUtils.newInstance(runnableClass);
				    boolean isMutex = scheduleDefEntity.isMutex();
				    if (!isMutex) scheduleDefEntity.getExtendValue().put("assisKey", PKUuid.create("ASSISKEY").toString());
				    if (runnable instanceof PKCornScheduleRunnable) {
			     		((PKCornScheduleRunnable)runnable).setService(service);
			       		((PKCornScheduleRunnable)runnable).setScheduleDefEntity(scheduleDefEntity);
			       		CronTask cronTask = new CronTask((Runnable) runnable, cron);
			       		ScheduledTask scheduledTask = taskRegistrar.scheduleCronTask(cronTask);
			       		cacheTask.add(scheduledTask);
				    } else throw PKDevException.throwException("后台事务执行类定义错误(编码=%s,执行类=%s).", scheduleDefEntity.getNumber(),scheduleDefEntity.getRunnableClass());
				}
			}
		}
	}
}
